// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/compiler/machine-graph-verifier.h"

#include "src/compiler/common-operator.h"
#include "src/compiler/graph.h"
#include "src/compiler/linkage.h"
#include "src/compiler/machine-operator.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/node.h"
#include "src/compiler/schedule.h"
#include "src/zone/zone.h"

#include "src/objects-inl.h" // weolar

namespace v8 {
namespace internal {
    namespace compiler {

        namespace {

            class MachineRepresentationInferrer {
            public:
                MachineRepresentationInferrer(Schedule const* schedule, Graph const* graph,
                    Linkage* linkage, Zone* zone)
                    : schedule_(schedule)
                    , linkage_(linkage)
                    , representation_vector_(graph->NodeCount(), MachineRepresentation::kNone,
                          zone)
                {
                    Run();
                }

                CallDescriptor* call_descriptor() const
                {
                    return linkage_->GetIncomingDescriptor();
                }

                MachineRepresentation GetRepresentation(Node const* node) const
                {
                    return representation_vector_.at(node->id());
                }

            private:
                MachineRepresentation GetProjectionType(Node const* projection)
                {
                    size_t index = ProjectionIndexOf(projection->op());
                    Node* input = projection->InputAt(0);
                    switch (input->opcode()) {
                    case IrOpcode::kInt32AddWithOverflow:
                    case IrOpcode::kInt32SubWithOverflow:
                    case IrOpcode::kInt32MulWithOverflow:
                        CHECK_LE(index, static_cast<size_t>(1));
                        return index == 0 ? MachineRepresentation::kWord32
                                          : MachineRepresentation::kBit;
                    case IrOpcode::kInt64AddWithOverflow:
                    case IrOpcode::kInt64SubWithOverflow:
                        CHECK_LE(index, static_cast<size_t>(1));
                        return index == 0 ? MachineRepresentation::kWord64
                                          : MachineRepresentation::kBit;
                    case IrOpcode::kTryTruncateFloat32ToInt64:
                    case IrOpcode::kTryTruncateFloat64ToInt64:
                    case IrOpcode::kTryTruncateFloat32ToUint64:
                        CHECK_LE(index, static_cast<size_t>(1));
                        return index == 0 ? MachineRepresentation::kWord64
                                          : MachineRepresentation::kBit;
                    case IrOpcode::kCall:
                    case IrOpcode::kCallWithCallerSavedRegisters: {
                        auto call_descriptor = CallDescriptorOf(input->op());
                        return call_descriptor->GetReturnType(index).representation();
                    }
                    case IrOpcode::kWord32AtomicPairLoad:
                    case IrOpcode::kWord32AtomicPairAdd:
                    case IrOpcode::kWord32AtomicPairSub:
                    case IrOpcode::kWord32AtomicPairAnd:
                    case IrOpcode::kWord32AtomicPairOr:
                    case IrOpcode::kWord32AtomicPairXor:
                    case IrOpcode::kWord32AtomicPairExchange:
                    case IrOpcode::kWord32AtomicPairCompareExchange:
                        CHECK_LE(index, static_cast<size_t>(1));
                        return MachineRepresentation::kWord32;
                    default:
                        return MachineRepresentation::kNone;
                    }
                }

                MachineRepresentation PromoteRepresentation(MachineRepresentation rep)
                {
                    switch (rep) {
                    case MachineRepresentation::kWord8:
                    case MachineRepresentation::kWord16:
                    case MachineRepresentation::kWord32:
                        return MachineRepresentation::kWord32;
                    default:
                        break;
                    }
                    return rep;
                }

                void Run()
                {
                    auto blocks = schedule_->all_blocks();
                    for (BasicBlock* block : *blocks) {
                        current_block_ = block;
                        for (size_t i = 0; i <= block->NodeCount(); ++i) {
                            Node const* node = i < block->NodeCount() ? block->NodeAt(i) : block->control_input();
                            if (node == nullptr) {
                                DCHECK_EQ(block->NodeCount(), i);
                                break;
                            }
                            switch (node->opcode()) {
                            case IrOpcode::kParameter:
                                representation_vector_[node->id()] = linkage_->GetParameterType(ParameterIndexOf(node->op()))
                                                                         .representation();
                                break;
                            case IrOpcode::kReturn: {
                                representation_vector_[node->id()] = PromoteRepresentation(
                                    linkage_->GetReturnType().representation());
                                break;
                            }
                            case IrOpcode::kProjection: {
                                representation_vector_[node->id()] = GetProjectionType(node);
                            } break;
                            case IrOpcode::kTypedStateValues:
                                representation_vector_[node->id()] = MachineRepresentation::kNone;
                                break;
                            case IrOpcode::kWord32AtomicLoad:
                            case IrOpcode::kWord64AtomicLoad:
                            case IrOpcode::kLoad:
                            case IrOpcode::kProtectedLoad:
                            case IrOpcode::kPoisonedLoad:
                                representation_vector_[node->id()] = PromoteRepresentation(
                                    LoadRepresentationOf(node->op()).representation());
                                break;
                            case IrOpcode::kLoadStackPointer:
                            case IrOpcode::kLoadFramePointer:
                            case IrOpcode::kLoadParentFramePointer:
                                representation_vector_[node->id()] = MachineType::PointerRepresentation();
                                break;
                            case IrOpcode::kUnalignedLoad:
                                representation_vector_[node->id()] = PromoteRepresentation(
                                    LoadRepresentationOf(node->op()).representation());
                                break;
                            case IrOpcode::kPhi:
                                representation_vector_[node->id()] = PhiRepresentationOf(node->op());
                                break;
                            case IrOpcode::kCall:
                            case IrOpcode::kCallWithCallerSavedRegisters: {
                                auto call_descriptor = CallDescriptorOf(node->op());
                                if (call_descriptor->ReturnCount() > 0) {
                                    representation_vector_[node->id()] = call_descriptor->GetReturnType(0).representation();
                                } else {
                                    representation_vector_[node->id()] = MachineRepresentation::kTagged;
                                }
                                break;
                            }
                            case IrOpcode::kWord32AtomicStore:
                            case IrOpcode::kWord64AtomicStore:
                                representation_vector_[node->id()] = PromoteRepresentation(AtomicStoreRepresentationOf(node->op()));
                                break;
                            case IrOpcode::kWord32AtomicPairLoad:
                            case IrOpcode::kWord32AtomicPairStore:
                            case IrOpcode::kWord32AtomicPairAdd:
                            case IrOpcode::kWord32AtomicPairSub:
                            case IrOpcode::kWord32AtomicPairAnd:
                            case IrOpcode::kWord32AtomicPairOr:
                            case IrOpcode::kWord32AtomicPairXor:
                            case IrOpcode::kWord32AtomicPairExchange:
                            case IrOpcode::kWord32AtomicPairCompareExchange:
                                representation_vector_[node->id()] = MachineRepresentation::kWord32;
                                break;
                            case IrOpcode::kWord32AtomicExchange:
                            case IrOpcode::kWord32AtomicCompareExchange:
                            case IrOpcode::kWord32AtomicAdd:
                            case IrOpcode::kWord32AtomicSub:
                            case IrOpcode::kWord32AtomicAnd:
                            case IrOpcode::kWord32AtomicOr:
                            case IrOpcode::kWord32AtomicXor:
                            case IrOpcode::kWord64AtomicExchange:
                            case IrOpcode::kWord64AtomicCompareExchange:
                            case IrOpcode::kWord64AtomicAdd:
                            case IrOpcode::kWord64AtomicSub:
                            case IrOpcode::kWord64AtomicAnd:
                            case IrOpcode::kWord64AtomicOr:
                            case IrOpcode::kWord64AtomicXor:
                                representation_vector_[node->id()] = PromoteRepresentation(
                                    AtomicOpType(node->op()).representation());
                                break;
                            case IrOpcode::kStore:
                            case IrOpcode::kProtectedStore:
                                representation_vector_[node->id()] = PromoteRepresentation(
                                    StoreRepresentationOf(node->op()).representation());
                                break;
                            case IrOpcode::kUnalignedStore:
                                representation_vector_[node->id()] = PromoteRepresentation(
                                    UnalignedStoreRepresentationOf(node->op()));
                                break;
                            case IrOpcode::kHeapConstant:
                            case IrOpcode::kNumberConstant:
                            case IrOpcode::kDelayedStringConstant:
                            case IrOpcode::kChangeBitToTagged:
                            case IrOpcode::kIfException:
                            case IrOpcode::kOsrValue:
                            case IrOpcode::kChangeInt32ToTagged:
                            case IrOpcode::kChangeUint32ToTagged:
                            case IrOpcode::kBitcastWordToTagged:
                            case IrOpcode::kTaggedPoisonOnSpeculation:
                                representation_vector_[node->id()] = MachineRepresentation::kTagged;
                                break;
                            case IrOpcode::kWord32PoisonOnSpeculation:
                                representation_vector_[node->id()] = MachineRepresentation::kWord32;
                                break;
                            case IrOpcode::kWord64PoisonOnSpeculation:
                                representation_vector_[node->id()] = MachineRepresentation::kWord64;
                                break;
                            case IrOpcode::kExternalConstant:
                                representation_vector_[node->id()] = MachineType::PointerRepresentation();
                                break;
                            case IrOpcode::kBitcastTaggedToWord:
                                representation_vector_[node->id()] = MachineType::PointerRepresentation();
                                break;
                            case IrOpcode::kBitcastWordToTaggedSigned:
                                representation_vector_[node->id()] = MachineRepresentation::kTaggedSigned;
                                break;
                            case IrOpcode::kWord32Equal:
                            case IrOpcode::kInt32LessThan:
                            case IrOpcode::kInt32LessThanOrEqual:
                            case IrOpcode::kUint32LessThan:
                            case IrOpcode::kUint32LessThanOrEqual:
                            case IrOpcode::kWord64Equal:
                            case IrOpcode::kInt64LessThan:
                            case IrOpcode::kInt64LessThanOrEqual:
                            case IrOpcode::kUint64LessThan:
                            case IrOpcode::kUint64LessThanOrEqual:
                            case IrOpcode::kFloat32Equal:
                            case IrOpcode::kFloat32LessThan:
                            case IrOpcode::kFloat32LessThanOrEqual:
                            case IrOpcode::kFloat64Equal:
                            case IrOpcode::kFloat64LessThan:
                            case IrOpcode::kFloat64LessThanOrEqual:
                            case IrOpcode::kChangeTaggedToBit:
                                representation_vector_[node->id()] = MachineRepresentation::kBit;
                                break;
#define LABEL(opcode) case IrOpcode::k##opcode:
                            case IrOpcode::kTruncateInt64ToInt32:
                            case IrOpcode::kTruncateFloat32ToInt32:
                            case IrOpcode::kTruncateFloat32ToUint32:
                            case IrOpcode::kBitcastFloat32ToInt32:
                            case IrOpcode::kI32x4ExtractLane:
                            case IrOpcode::kI16x8ExtractLane:
                            case IrOpcode::kI8x16ExtractLane:
                            case IrOpcode::kInt32Constant:
                            case IrOpcode::kRelocatableInt32Constant:
                            case IrOpcode::kTruncateFloat64ToWord32:
                            case IrOpcode::kTruncateFloat64ToUint32:
                            case IrOpcode::kChangeFloat64ToInt32:
                            case IrOpcode::kChangeFloat64ToUint32:
                            case IrOpcode::kRoundFloat64ToInt32:
                            case IrOpcode::kFloat64ExtractLowWord32:
                            case IrOpcode::kFloat64ExtractHighWord32:
                                MACHINE_UNOP_32_LIST(LABEL)
                                MACHINE_BINOP_32_LIST(LABEL)
                                {
                                    representation_vector_[node->id()] = MachineRepresentation::kWord32;
                                }
                                break;
                            case IrOpcode::kChangeInt32ToInt64:
                            case IrOpcode::kChangeUint32ToUint64:
                            case IrOpcode::kInt64Constant:
                            case IrOpcode::kRelocatableInt64Constant:
                            case IrOpcode::kBitcastFloat64ToInt64:
                            case IrOpcode::kChangeFloat64ToUint64:
                                MACHINE_BINOP_64_LIST(LABEL)
                                {
                                    representation_vector_[node->id()] = MachineRepresentation::kWord64;
                                }
                                break;
                            case IrOpcode::kRoundInt32ToFloat32:
                            case IrOpcode::kRoundUint32ToFloat32:
                            case IrOpcode::kRoundInt64ToFloat32:
                            case IrOpcode::kRoundUint64ToFloat32:
                            case IrOpcode::kBitcastInt32ToFloat32:
                            case IrOpcode::kFloat32Constant:
                            case IrOpcode::kTruncateFloat64ToFloat32:
                                MACHINE_FLOAT32_BINOP_LIST(LABEL)
                                MACHINE_FLOAT32_UNOP_LIST(LABEL)
                                {
                                    representation_vector_[node->id()] = MachineRepresentation::kFloat32;
                                }
                                break;
                            case IrOpcode::kRoundInt64ToFloat64:
                            case IrOpcode::kRoundUint64ToFloat64:
                            case IrOpcode::kChangeFloat32ToFloat64:
                            case IrOpcode::kChangeInt32ToFloat64:
                            case IrOpcode::kChangeUint32ToFloat64:
                            case IrOpcode::kFloat64InsertLowWord32:
                            case IrOpcode::kFloat64InsertHighWord32:
                            case IrOpcode::kFloat64Constant:
                            case IrOpcode::kFloat64SilenceNaN:
                                MACHINE_FLOAT64_BINOP_LIST(LABEL)
                                MACHINE_FLOAT64_UNOP_LIST(LABEL)
                                {
                                    representation_vector_[node->id()] = MachineRepresentation::kFloat64;
                                }
                                break;
                            case IrOpcode::kI32x4ReplaceLane:
                            case IrOpcode::kI32x4Splat:
                                representation_vector_[node->id()] = MachineRepresentation::kSimd128;
                                break;
#undef LABEL
                            default:
                                break;
                            }
                        }
                    }
                }

                Schedule const* const schedule_;
                Linkage const* const linkage_;
                ZoneVector<MachineRepresentation> representation_vector_;
                BasicBlock* current_block_;
            };

            class MachineRepresentationChecker {
            public:
                MachineRepresentationChecker(
                    Schedule const* const schedule,
                    MachineRepresentationInferrer const* const inferrer, bool is_stub,
                    const char* name)
                    : schedule_(schedule)
                    , inferrer_(inferrer)
                    , is_stub_(is_stub)
                    , name_(name)
                    , current_block_(nullptr)
                {
                }

                void Run()
                {
                    BasicBlockVector const* blocks = schedule_->all_blocks();
                    for (BasicBlock* block : *blocks) {
                        current_block_ = block;
                        for (size_t i = 0; i <= block->NodeCount(); ++i) {
                            Node const* node = i < block->NodeCount() ? block->NodeAt(i) : block->control_input();
                            if (node == nullptr) {
                                DCHECK_EQ(block->NodeCount(), i);
                                break;
                            }
                            switch (node->opcode()) {
                            case IrOpcode::kCall:
                            case IrOpcode::kCallWithCallerSavedRegisters:
                            case IrOpcode::kTailCall:
                                CheckCallInputs(node);
                                break;
                            case IrOpcode::kChangeBitToTagged:
                                CHECK_EQ(MachineRepresentation::kBit,
                                    inferrer_->GetRepresentation(node->InputAt(0)));
                                break;
                            case IrOpcode::kChangeTaggedToBit:
                                CHECK_EQ(MachineRepresentation::kTagged,
                                    inferrer_->GetRepresentation(node->InputAt(0)));
                                break;
                            case IrOpcode::kRoundInt64ToFloat64:
                            case IrOpcode::kRoundUint64ToFloat64:
                            case IrOpcode::kRoundInt64ToFloat32:
                            case IrOpcode::kRoundUint64ToFloat32:
                            case IrOpcode::kTruncateInt64ToInt32:
                                CheckValueInputForInt64Op(node, 0);
                                break;
                            case IrOpcode::kBitcastWordToTagged:
                            case IrOpcode::kBitcastWordToTaggedSigned:
                                CheckValueInputRepresentationIs(
                                    node, 0, MachineType::PointerRepresentation());
                                break;
                            case IrOpcode::kWord32PoisonOnSpeculation:
                                CheckValueInputRepresentationIs(node, 0,
                                    MachineRepresentation::kWord32);
                                break;
                            case IrOpcode::kWord64PoisonOnSpeculation:
                                CheckValueInputRepresentationIs(node, 0,
                                    MachineRepresentation::kWord64);
                                break;
                            case IrOpcode::kBitcastTaggedToWord:
                            case IrOpcode::kTaggedPoisonOnSpeculation:
                                CheckValueInputIsTagged(node, 0);
                                break;
                            case IrOpcode::kTruncateFloat64ToWord32:
                            case IrOpcode::kTruncateFloat64ToUint32:
                            case IrOpcode::kTruncateFloat64ToFloat32:
                            case IrOpcode::kChangeFloat64ToInt32:
                            case IrOpcode::kChangeFloat64ToUint32:
                            case IrOpcode::kRoundFloat64ToInt32:
                            case IrOpcode::kFloat64ExtractLowWord32:
                            case IrOpcode::kFloat64ExtractHighWord32:
                            case IrOpcode::kBitcastFloat64ToInt64:
                            case IrOpcode::kTryTruncateFloat64ToInt64:
                                CheckValueInputForFloat64Op(node, 0);
                                break;
                            case IrOpcode::kWord64Equal:
                                if (Is64()) {
                                    CheckValueInputIsTaggedOrPointer(node, 0);
                                    CheckValueInputIsTaggedOrPointer(node, 1);
                                    if (!is_stub_) {
                                        CheckValueInputRepresentationIs(
                                            node, 1, inferrer_->GetRepresentation(node->InputAt(0)));
                                    }
                                } else {
                                    CheckValueInputForInt64Op(node, 0);
                                    CheckValueInputForInt64Op(node, 1);
                                }
                                break;
                            case IrOpcode::kInt64LessThan:
                            case IrOpcode::kInt64LessThanOrEqual:
                            case IrOpcode::kUint64LessThan:
                            case IrOpcode::kUint64LessThanOrEqual:
                                CheckValueInputForInt64Op(node, 0);
                                CheckValueInputForInt64Op(node, 1);
                                break;
                            case IrOpcode::kI32x4ExtractLane:
                            case IrOpcode::kI16x8ExtractLane:
                            case IrOpcode::kI8x16ExtractLane:
                                CheckValueInputRepresentationIs(node, 0,
                                    MachineRepresentation::kSimd128);
                                break;
                            case IrOpcode::kI32x4ReplaceLane:
                                CheckValueInputRepresentationIs(node, 0,
                                    MachineRepresentation::kSimd128);
                                CheckValueInputForInt32Op(node, 1);
                                break;
                            case IrOpcode::kI32x4Splat:
                                CheckValueInputForInt32Op(node, 0);
                                break;
#define LABEL(opcode) case IrOpcode::k##opcode:
                            case IrOpcode::kChangeInt32ToTagged:
                            case IrOpcode::kChangeUint32ToTagged:
                            case IrOpcode::kChangeInt32ToFloat64:
                            case IrOpcode::kChangeUint32ToFloat64:
                            case IrOpcode::kRoundInt32ToFloat32:
                            case IrOpcode::kRoundUint32ToFloat32:
                            case IrOpcode::kBitcastInt32ToFloat32:
                            case IrOpcode::kChangeInt32ToInt64:
                            case IrOpcode::kChangeUint32ToUint64:
                                MACHINE_UNOP_32_LIST(LABEL) { CheckValueInputForInt32Op(node, 0); }
                                break;
                            case IrOpcode::kWord32Equal:
                                if (Is32()) {
                                    CheckValueInputIsTaggedOrPointer(node, 0);
                                    CheckValueInputIsTaggedOrPointer(node, 1);
                                    if (!is_stub_) {
                                        CheckValueInputRepresentationIs(
                                            node, 1, inferrer_->GetRepresentation(node->InputAt(0)));
                                    }
                                } else {
                                    CheckValueInputForInt32Op(node, 0);
                                    CheckValueInputForInt32Op(node, 1);
                                }
                                break;

                            case IrOpcode::kInt32LessThan:
                            case IrOpcode::kInt32LessThanOrEqual:
                            case IrOpcode::kUint32LessThan:
                            case IrOpcode::kUint32LessThanOrEqual:
                                MACHINE_BINOP_32_LIST(LABEL)
                                {
                                    CheckValueInputForInt32Op(node, 0);
                                    CheckValueInputForInt32Op(node, 1);
                                }
                                break;
                                MACHINE_BINOP_64_LIST(LABEL)
                                {
                                    CheckValueInputForInt64Op(node, 0);
                                    CheckValueInputForInt64Op(node, 1);
                                }
                                break;
                            case IrOpcode::kFloat32Equal:
                            case IrOpcode::kFloat32LessThan:
                            case IrOpcode::kFloat32LessThanOrEqual:
                                MACHINE_FLOAT32_BINOP_LIST(LABEL)
                                {
                                    CheckValueInputForFloat32Op(node, 0);
                                    CheckValueInputForFloat32Op(node, 1);
                                }
                                break;
                            case IrOpcode::kChangeFloat32ToFloat64:
                            case IrOpcode::kTruncateFloat32ToInt32:
                            case IrOpcode::kTruncateFloat32ToUint32:
                            case IrOpcode::kBitcastFloat32ToInt32:
                                MACHINE_FLOAT32_UNOP_LIST(LABEL)
                                {
                                    CheckValueInputForFloat32Op(node, 0);
                                }
                                break;
                            case IrOpcode::kFloat64Equal:
                            case IrOpcode::kFloat64LessThan:
                            case IrOpcode::kFloat64LessThanOrEqual:
                                MACHINE_FLOAT64_BINOP_LIST(LABEL)
                                {
                                    CheckValueInputForFloat64Op(node, 0);
                                    CheckValueInputForFloat64Op(node, 1);
                                }
                                break;
                            case IrOpcode::kFloat64SilenceNaN:
                            case IrOpcode::kChangeFloat64ToUint64:
                                MACHINE_FLOAT64_UNOP_LIST(LABEL)
                                {
                                    CheckValueInputForFloat64Op(node, 0);
                                }
                                break;
#undef LABEL
                            case IrOpcode::kFloat64InsertLowWord32:
                            case IrOpcode::kFloat64InsertHighWord32:
                                CheckValueInputForFloat64Op(node, 0);
                                CheckValueInputForInt32Op(node, 1);
                                break;
                            case IrOpcode::kParameter:
                            case IrOpcode::kProjection:
                                break;
                            case IrOpcode::kDebugAbort:
                                CheckValueInputIsTagged(node, 0);
                                break;
                            case IrOpcode::kLoad:
                            case IrOpcode::kWord32AtomicLoad:
                            case IrOpcode::kWord32AtomicPairLoad:
                            case IrOpcode::kWord64AtomicLoad:
                            case IrOpcode::kPoisonedLoad:
                                CheckValueInputIsTaggedOrPointer(node, 0);
                                CheckValueInputRepresentationIs(
                                    node, 1, MachineType::PointerRepresentation());
                                break;
                            case IrOpcode::kWord32AtomicPairAdd:
                            case IrOpcode::kWord32AtomicPairSub:
                            case IrOpcode::kWord32AtomicPairAnd:
                            case IrOpcode::kWord32AtomicPairOr:
                            case IrOpcode::kWord32AtomicPairXor:
                            case IrOpcode::kWord32AtomicPairStore:
                            case IrOpcode::kWord32AtomicPairExchange:
                                CheckValueInputRepresentationIs(node, 3,
                                    MachineRepresentation::kWord32);
                                V8_FALLTHROUGH;
                            case IrOpcode::kStore:
                            case IrOpcode::kWord32AtomicStore:
                            case IrOpcode::kWord32AtomicExchange:
                            case IrOpcode::kWord32AtomicAdd:
                            case IrOpcode::kWord32AtomicSub:
                            case IrOpcode::kWord32AtomicAnd:
                            case IrOpcode::kWord32AtomicOr:
                            case IrOpcode::kWord32AtomicXor:
                            case IrOpcode::kWord64AtomicStore:
                            case IrOpcode::kWord64AtomicExchange:
                            case IrOpcode::kWord64AtomicAdd:
                            case IrOpcode::kWord64AtomicSub:
                            case IrOpcode::kWord64AtomicAnd:
                            case IrOpcode::kWord64AtomicOr:
                            case IrOpcode::kWord64AtomicXor:
                                CheckValueInputIsTaggedOrPointer(node, 0);
                                CheckValueInputRepresentationIs(
                                    node, 1, MachineType::PointerRepresentation());
                                switch (inferrer_->GetRepresentation(node)) {
                                case MachineRepresentation::kTagged:
                                case MachineRepresentation::kTaggedPointer:
                                case MachineRepresentation::kTaggedSigned:
                                    CheckValueInputIsTagged(node, 2);
                                    break;
                                default:
                                    CheckValueInputRepresentationIs(
                                        node, 2, inferrer_->GetRepresentation(node));
                                }
                                break;
                            case IrOpcode::kWord32AtomicPairCompareExchange:
                                CheckValueInputRepresentationIs(node, 4,
                                    MachineRepresentation::kWord32);
                                CheckValueInputRepresentationIs(node, 5,
                                    MachineRepresentation::kWord32);
                                V8_FALLTHROUGH;
                            case IrOpcode::kWord32AtomicCompareExchange:
                            case IrOpcode::kWord64AtomicCompareExchange:
                                CheckValueInputIsTaggedOrPointer(node, 0);
                                CheckValueInputRepresentationIs(
                                    node, 1, MachineType::PointerRepresentation());
                                switch (inferrer_->GetRepresentation(node)) {
                                case MachineRepresentation::kTagged:
                                case MachineRepresentation::kTaggedPointer:
                                case MachineRepresentation::kTaggedSigned:
                                    CheckValueInputIsTagged(node, 2);
                                    CheckValueInputIsTagged(node, 3);
                                    break;
                                default:
                                    CheckValueInputRepresentationIs(
                                        node, 2, inferrer_->GetRepresentation(node));
                                    CheckValueInputRepresentationIs(
                                        node, 3, inferrer_->GetRepresentation(node));
                                }
                                break;
                            case IrOpcode::kPhi:
                                switch (inferrer_->GetRepresentation(node)) {
                                case MachineRepresentation::kTagged:
                                case MachineRepresentation::kTaggedPointer:
                                case MachineRepresentation::kTaggedSigned:
                                    for (int i = 0; i < node->op()->ValueInputCount(); ++i) {
                                        CheckValueInputIsTagged(node, i);
                                    }
                                    break;
                                case MachineRepresentation::kWord32:
                                    for (int i = 0; i < node->op()->ValueInputCount(); ++i) {
                                        CheckValueInputForInt32Op(node, i);
                                    }
                                    break;
                                default:
                                    for (int i = 0; i < node->op()->ValueInputCount(); ++i) {
                                        CheckValueInputRepresentationIs(
                                            node, i, inferrer_->GetRepresentation(node));
                                    }
                                    break;
                                }
                                break;
                            case IrOpcode::kBranch:
                            case IrOpcode::kSwitch:
                                CheckValueInputForInt32Op(node, 0);
                                break;
                            case IrOpcode::kReturn: {
                                // TODO(ishell): enable once the pop count parameter type becomes
                                // MachineType::PointerRepresentation(). Currently it's int32 or
                                // word-size.
                                // CheckValueInputRepresentationIs(
                                //     node, 0, MachineType::PointerRepresentation());  // Pop count
                                size_t return_count = inferrer_->call_descriptor()->ReturnCount();
                                for (size_t i = 0; i < return_count; i++) {
                                    MachineType type = inferrer_->call_descriptor()->GetReturnType(i);
                                    int input_index = static_cast<int>(i + 1);
                                    switch (type.representation()) {
                                    case MachineRepresentation::kTagged:
                                    case MachineRepresentation::kTaggedPointer:
                                    case MachineRepresentation::kTaggedSigned:
                                        CheckValueInputIsTagged(node, input_index);
                                        break;
                                    case MachineRepresentation::kWord32:
                                        CheckValueInputForInt32Op(node, input_index);
                                        break;
                                    default:
                                        CheckValueInputRepresentationIs(node, input_index,
                                            type.representation());
                                        break;
                                    }
                                }
                                break;
                            }
                            case IrOpcode::kThrow:
                            case IrOpcode::kTypedStateValues:
                            case IrOpcode::kFrameState:
                                break;
                            default:
                                if (node->op()->ValueInputCount() != 0) {
                                    std::stringstream str;
                                    str << "Node #" << node->id() << ":" << *node->op()
                                        << " in the machine graph is not being checked.";
                                    PrintDebugHelp(str, node);
                                    FATAL("%s", str.str().c_str());
                                }
                                break;
                            }
                        }
                    }
                }

            private:
                static bool Is32()
                {
                    return MachineType::PointerRepresentation() == MachineRepresentation::kWord32;
                }
                static bool Is64()
                {
                    return MachineType::PointerRepresentation() == MachineRepresentation::kWord64;
                }

                void CheckValueInputRepresentationIs(Node const* node, int index,
                    MachineRepresentation representation)
                {
                    Node const* input = node->InputAt(index);
                    MachineRepresentation input_representation = inferrer_->GetRepresentation(input);
                    if (input_representation != representation) {
                        std::stringstream str;
                        str << "TypeError: node #" << node->id() << ":" << *node->op()
                            << " uses node #" << input->id() << ":" << *input->op() << ":"
                            << input_representation << " which doesn't have a " << representation
                            << " representation.";
                        PrintDebugHelp(str, node);
                        FATAL("%s", str.str().c_str());
                    }
                }

                void CheckValueInputIsTagged(Node const* node, int index)
                {
                    Node const* input = node->InputAt(index);
                    switch (inferrer_->GetRepresentation(input)) {
                    case MachineRepresentation::kTagged:
                    case MachineRepresentation::kTaggedPointer:
                    case MachineRepresentation::kTaggedSigned:
                        return;
                    default:
                        break;
                    }
                    std::ostringstream str;
                    str << "TypeError: node #" << node->id() << ":" << *node->op()
                        << " uses node #" << input->id() << ":" << *input->op()
                        << " which doesn't have a tagged representation.";
                    PrintDebugHelp(str, node);
                    FATAL("%s", str.str().c_str());
                }

                void CheckValueInputIsTaggedOrPointer(Node const* node, int index)
                {
                    Node const* input = node->InputAt(index);
                    switch (inferrer_->GetRepresentation(input)) {
                    case MachineRepresentation::kTagged:
                    case MachineRepresentation::kTaggedPointer:
                    case MachineRepresentation::kTaggedSigned:
                        return;
                    case MachineRepresentation::kBit:
                    case MachineRepresentation::kWord8:
                    case MachineRepresentation::kWord16:
                    case MachineRepresentation::kWord32:
                        if (Is32()) {
                            return;
                        }
                        break;
                    case MachineRepresentation::kWord64:
                        if (Is64()) {
                            return;
                        }
                        break;
                    default:
                        break;
                    }
                    if (inferrer_->GetRepresentation(input) != MachineType::PointerRepresentation()) {
                        std::ostringstream str;
                        str << "TypeError: node #" << node->id() << ":" << *node->op()
                            << " uses node #" << input->id() << ":" << *input->op()
                            << " which doesn't have a tagged or pointer representation.";
                        PrintDebugHelp(str, node);
                        FATAL("%s", str.str().c_str());
                    }
                }

                void CheckValueInputForInt32Op(Node const* node, int index)
                {
                    Node const* input = node->InputAt(index);
                    switch (inferrer_->GetRepresentation(input)) {
                    case MachineRepresentation::kBit:
                    case MachineRepresentation::kWord8:
                    case MachineRepresentation::kWord16:
                    case MachineRepresentation::kWord32:
                        return;
                    case MachineRepresentation::kNone: {
                        std::ostringstream str;
                        str << "TypeError: node #" << input->id() << ":" << *input->op()
                            << " is untyped.";
                        PrintDebugHelp(str, node);
                        FATAL("%s", str.str().c_str());
                        break;
                    }
                    default:
                        break;
                    }
                    std::ostringstream str;
                    str << "TypeError: node #" << node->id() << ":" << *node->op()
                        << " uses node #" << input->id() << ":" << *input->op()
                        << " which doesn't have an int32-compatible representation.";
                    PrintDebugHelp(str, node);
                    FATAL("%s", str.str().c_str());
                }

                void CheckValueInputForInt64Op(Node const* node, int index)
                {
                    Node const* input = node->InputAt(index);
                    MachineRepresentation input_representation = inferrer_->GetRepresentation(input);
                    switch (input_representation) {
                    case MachineRepresentation::kWord64:
                        return;
                    case MachineRepresentation::kNone: {
                        std::ostringstream str;
                        str << "TypeError: node #" << input->id() << ":" << *input->op()
                            << " is untyped.";
                        PrintDebugHelp(str, node);
                        FATAL("%s", str.str().c_str());
                        break;
                    }

                    default:
                        break;
                    }
                    std::ostringstream str;
                    str << "TypeError: node #" << node->id() << ":" << *node->op()
                        << " uses node #" << input->id() << ":" << *input->op() << ":"
                        << input_representation
                        << " which doesn't have a kWord64 representation.";
                    PrintDebugHelp(str, node);
                    FATAL("%s", str.str().c_str());
                }

                void CheckValueInputForFloat32Op(Node const* node, int index)
                {
                    Node const* input = node->InputAt(index);
                    if (MachineRepresentation::kFloat32 == inferrer_->GetRepresentation(input)) {
                        return;
                    }
                    std::ostringstream str;
                    str << "TypeError: node #" << node->id() << ":" << *node->op()
                        << " uses node #" << input->id() << ":" << *input->op()
                        << " which doesn't have a kFloat32 representation.";
                    PrintDebugHelp(str, node);
                    FATAL("%s", str.str().c_str());
                }

                void CheckValueInputForFloat64Op(Node const* node, int index)
                {
                    Node const* input = node->InputAt(index);
                    if (MachineRepresentation::kFloat64 == inferrer_->GetRepresentation(input)) {
                        return;
                    }
                    std::ostringstream str;
                    str << "TypeError: node #" << node->id() << ":" << *node->op()
                        << " uses node #" << input->id() << ":" << *input->op()
                        << " which doesn't have a kFloat64 representation.";
                    PrintDebugHelp(str, node);
                    FATAL("%s", str.str().c_str());
                }

                void CheckCallInputs(Node const* node)
                {
                    auto call_descriptor = CallDescriptorOf(node->op());
                    std::ostringstream str;
                    bool should_log_error = false;
                    for (size_t i = 0; i < call_descriptor->InputCount(); ++i) {
                        Node const* input = node->InputAt(static_cast<int>(i));
                        MachineRepresentation const input_type = inferrer_->GetRepresentation(input);
                        MachineRepresentation const expected_input_type = call_descriptor->GetInputType(i).representation();
                        if (!IsCompatible(expected_input_type, input_type)) {
                            if (!should_log_error) {
                                should_log_error = true;
                                str << "TypeError: node #" << node->id() << ":" << *node->op()
                                    << " has wrong type for:" << std::endl;
                            } else {
                                str << std::endl;
                            }
                            str << " * input " << i << " (" << input->id() << ":" << *input->op()
                                << ") has a " << input_type
                                << " representation (expected: " << expected_input_type << ").";
                        }
                    }
                    if (should_log_error) {
                        PrintDebugHelp(str, node);
                        FATAL("%s", str.str().c_str());
                    }
                }

                bool Intersect(MachineRepresentation lhs, MachineRepresentation rhs)
                {
                    return (GetRepresentationProperties(lhs) & GetRepresentationProperties(rhs)) != 0;
                }

                enum RepresentationProperties { kIsPointer = 1,
                    kIsTagged = 2 };

                int GetRepresentationProperties(MachineRepresentation representation)
                {
                    switch (representation) {
                    case MachineRepresentation::kTagged:
                    case MachineRepresentation::kTaggedPointer:
                        return kIsPointer | kIsTagged;
                    case MachineRepresentation::kTaggedSigned:
                        return kIsTagged;
                    case MachineRepresentation::kWord32:
                        return MachineRepresentation::kWord32 == MachineType::PointerRepresentation()
                            ? kIsPointer
                            : 0;
                    case MachineRepresentation::kWord64:
                        return MachineRepresentation::kWord64 == MachineType::PointerRepresentation()
                            ? kIsPointer
                            : 0;
                    default:
                        return 0;
                    }
                }

                bool IsCompatible(MachineRepresentation expected,
                    MachineRepresentation actual)
                {
                    switch (expected) {
                    case MachineRepresentation::kTagged:
                        return (actual == MachineRepresentation::kTagged || actual == MachineRepresentation::kTaggedSigned || actual == MachineRepresentation::kTaggedPointer);
                    case MachineRepresentation::kCompressed:
                        return (actual == MachineRepresentation::kCompressed || actual == MachineRepresentation::kCompressedSigned || actual == MachineRepresentation::kCompressedPointer);
                    case MachineRepresentation::kTaggedSigned:
                    case MachineRepresentation::kTaggedPointer:
                    case MachineRepresentation::kCompressedSigned:
                    case MachineRepresentation::kCompressedPointer:
                    case MachineRepresentation::kFloat32:
                    case MachineRepresentation::kFloat64:
                    case MachineRepresentation::kSimd128:
                    case MachineRepresentation::kBit:
                    case MachineRepresentation::kWord8:
                    case MachineRepresentation::kWord16:
                    case MachineRepresentation::kWord64:
                        return expected == actual;
                        break;
                    case MachineRepresentation::kWord32:
                        return (actual == MachineRepresentation::kBit || actual == MachineRepresentation::kWord8 || actual == MachineRepresentation::kWord16 || actual == MachineRepresentation::kWord32);
                    case MachineRepresentation::kNone:
                        UNREACHABLE();
                    }
                    return false;
                }

                void PrintDebugHelp(std::ostream& out, Node const* node)
                {
                    if (DEBUG_BOOL) {
                        out << "\n#     Current block: " << *current_block_;
                        out << "\n#\n#     Specify option --csa-trap-on-node=" << name_ << ","
                            << node->id() << " for debugging.";
                    }
                }

                Schedule const* const schedule_;
                MachineRepresentationInferrer const* const inferrer_;
                bool is_stub_;
                const char* name_;
                BasicBlock* current_block_;
            };

        } // namespace

        void MachineGraphVerifier::Run(Graph* graph, Schedule const* const schedule,
            Linkage* linkage, bool is_stub, const char* name,
            Zone* temp_zone)
        {
            MachineRepresentationInferrer representation_inferrer(schedule, graph,
                linkage, temp_zone);
            MachineRepresentationChecker checker(schedule, &representation_inferrer,
                is_stub, name);
            checker.Run();
        }

    } // namespace compiler
} // namespace internal
} // namespace v8
